package controller;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;

import dataStructure.LinkedList;
import dataStructure.ListNode;

import entity.BookingRecord;
import entity.FileAsso;
import entity.Member;
import entity.RedemptionRecord;
import entity.Staff;
import entity.Venue;

public class FileIOController {
	private LinkedList<FileAsso> fileAssoList;

	public FileIOController() {
		this.fileAssoList = new LinkedList<FileAsso>();
	}

	public FileIOController(LinkedList<FileAsso> fAList) {
		this.fileAssoList = fAList;
	}

	@SuppressWarnings({ "unchecked", "resource" })
	public boolean inputData(LinkedList<?> destination, String type) throws IOException, ParseException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NullPointerException {
		// Search FileAsso according to type read line by line, and addNode to destination Done.

		if (this.fileAssoList.getLength() == 0) {
			// No defined file Asso is defined.
			System.out.println("No file and data association is defined in FileIOController Class.");
			return false;
		} else {
			String f2Read = this.getFAssoByIdentifier(type);
			if (f2Read == null) {
				// Msg: File name defined is wrong.
				return false;
			}

			// start input file stream
			File inFile = new File(f2Read);
			BufferedReader reader = new BufferedReader(new FileReader(inFile));

			String line = null;
			while ((line = reader.readLine()) != null) {
				// pass a string to different data type and split accordingly, return the memory address to the linked list.

				@SuppressWarnings("rawtypes")
				ListNode newNode = this.getNewNode(type, line);
				if (newNode == null) {
					// Msg: Either type defined is wrong or line read is wrong.
					return false;
				}
				destination.addNode(newNode);
			}

			reader.close();

			return true;
		}
	}

	private ListNode<?> getNewNode(String type, String data) throws ParseException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		if (type.equals("member")) {
			Member newMemberNode = new Member(data);
			return new ListNode<Member>(newMemberNode);
		} else if (type.equals("bookingRecord")) {
			BookingRecord newRecordNode = new BookingRecord(data);
			return new ListNode<BookingRecord>(newRecordNode);
		} else if (type.equals("redemptionRecord")) {
			RedemptionRecord newRecordNode = new RedemptionRecord(data);
			return new ListNode<RedemptionRecord>(newRecordNode);
		} else if (type.equals("venue")) {
			Venue newVenueNode = new Venue(data);
			return new ListNode<Venue>(newVenueNode);
		} else if (type.equals("staff")) {
			Staff newStaffNode = new Staff(data);
			return new ListNode<Staff>(newStaffNode);
		}
		return null;
	}

	public boolean outputDataToFile(LinkedList<?> list, String identifier) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NullPointerException, IOException, ParseException {
		String f2Write = this.getFAssoByIdentifier(identifier);
		if (f2Write == null) {
			return false;
		}

		FileWriter outputFile = new FileWriter(f2Write);
		BufferedWriter bWriter = new BufferedWriter(outputFile);
		for (int i = 0; i < list.getLength(); i++) {
			String nodeContentStr = "";
			if (identifier.equals("member")) {
				Member temp = (Member) list.get(i);
				nodeContentStr = temp.getContentStr();
			} else if (identifier.equals("bookingRecord")) {
				BookingRecord temp = (BookingRecord) list.get(i);
				nodeContentStr = temp.getContentStr();
			} else if (identifier.equals("redemptionRecord")) {
				RedemptionRecord temp = (RedemptionRecord) list.get(i);
				nodeContentStr = temp.getContentStr();
			} else if (identifier.equals("venue")) {
				Venue temp = (Venue) list.get(i);
				nodeContentStr = temp.getContentStr();
			}
			
			bWriter.write(nodeContentStr);
			bWriter.newLine();
		}

		bWriter.close();
		return true;
	}

	private String getFAssoByIdentifier(String type) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NullPointerException {
		// this function will return the file name that associated with the data defined in constructor before.
		FileAsso fAsso = this.fileAssoList.searchContent("dataIdentifer", type);
		if (fAsso != null) {
			return fAsso.getFileAssoName();
		}
		return null;
	}

	public void setFileAssoList(LinkedList<FileAsso> fileAssoList) {
		this.fileAssoList = fileAssoList;
	}

	public LinkedList<FileAsso> getFileAssoList() {
		return this.fileAssoList;
	}
}